package com.baturu.vin.ford.service.impl;

import com.baturu.carvin.dtos.CarTypeDTO;
import com.baturu.carvin.service.CarTypeService;
import com.baturu.vin.base.constant.MySQLVariables;
import com.baturu.vin.base.dto.BasePartsImageInfoDTO;
import com.baturu.vin.base.service.impl.EPCAnalyser;
import com.baturu.vin.base.util.MD5Utils;
import com.baturu.vin.dto.*;
import com.baturu.vin.dto.parts.PartsAssemblyDTO;
import com.baturu.vin.ford.dal.dao.*;
import com.baturu.vin.ford.dal.entity.*;
import com.baturu.vin.ford.dto.FordImageDTO;
import com.baturu.vin.ford.dto.FordReplaceDTO;
import com.baturu.vin.ford.dto.FordSortImageDTO;
import com.baturu.vin.ford.dto.FordVinAnalyserResultDTO;
import com.baturu.vin.kit.StreamKit;
import com.baturu.vin.service.EPCVinCacheService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service("fordEPCAnalyser")
public class FordEPCAnalyser extends EPCAnalyser<FordVinAnalyserResultDTO, FordPartsMappingEntity, FordPartsInfoEntity, EPCCarTypeDTO> implements InitializingBean {

    @Autowired
    private FordVinInfoDAO vinInfoDAO;

    @Autowired
    private FordPartsMappingDAO partsMappingDAO;

    @Autowired
    private FordPartsInfoDAO partsInfoDAO;

    @Autowired
    private FordCommonDAO commonDAO;

    @Autowired
    private FordImageDAO imageDAO;

    @Autowired
    private EPCVinCacheService vinCacheService;

    @Autowired
    private CarTypeService carTypeService;
    @Autowired
    private FordAssemblyDAO assemblyDAO;

    private Map<String, String> majorOptionMapper = Collections.emptyMap();

    private List<String> optionForArea = Lists.newArrayList();

    private Map<String, String> versionOptionMapper = Collections.emptyMap();

    private Map<String, String> colorMapper = Collections.emptyMap();

    private Map<String, String> featureMapper = Collections.emptyMap();

    private void initMajorOptionMapper() {
        // attribute 解释信息其实相当于 major option
        Set<Map<String, String>> attributes = commonDAO.queryAttributeAll();
        majorOptionMapper = attributes.stream()
                .filter(m -> StringUtils.isNotBlank(m.get("attributeCode")) && StringUtils.isNotBlank(m.get("attributeLabel")))
                .collect(Collectors.toMap(m -> m.get("attributeCode"), m -> m.get("attributeLabel")));
    }

    private void initVersionOptionMapper() {
        // attribute 解释信息其实相当于 major option
        List<Map<String, String>> attributes = commonDAO.queryVersionTranslate();
        versionOptionMapper = attributes.stream()
                .filter(m -> StringUtils.isNotBlank(m.get("enLabel")) && StringUtils.isNotBlank(m.get("chLabel")))
                .collect(Collectors.toMap(m -> m.get("enLabel"), m -> m.get("chLabel")));
    }

    private void initColorFeatureMapper() {
        //颜色功能备注
        List<Map<String, String>> colorFeatureMapping = commonDAO.queryColorFeatureMapping();
        colorMapper = colorFeatureMapping.stream().filter(m->StringUtils.isNotBlank(m.get("description")) && StringUtils.isNotBlank(m.get("color")))
                .collect(Collectors.toMap(m -> m.get("description"), m -> m.get("color")));

        featureMapper = colorFeatureMapping.stream().filter(m->StringUtils.isNotBlank(m.get("description")) && StringUtils.isNotBlank(m.get("feature")))
                .collect(Collectors.toMap(m -> m.get("description"), m -> m.get("feature")));
    }

    @Override
    public FordVinAnalyserResultDTO analyseVinCode(String vinCode) {
        FordVinInfoEntity vinInfo = vinInfoDAO.queryByVinCode(vinCode);
        if (vinInfo == null) {
            log.info("暂无车架号信息缓存结果：{}", vinCode);
            vinCacheService.addWaitCacheVinCode(EPCAnalyserEnum.FORD, vinCode);
            return null;
        }

        String[] interpretationAVSDataArr = StringUtils.split(vinInfo.getInterpretationAVSData(), ";");
        Set<String> optionCodes = Sets.newHashSet(interpretationAVSDataArr);
        Set<String> majorOptionCodes = optionCodes.stream().filter(code -> {
            String[] split = code.split("\\|");
            return split.length > 1 && majorOptionMapper.keySet().contains(split[0]);
        }).collect(Collectors.toSet());

        List<Map<String, String>> optionLabels = getOptionLabelsByAnalyseResult(vinInfo);

        Set<String> confirmLabels = getConfirmLabels(optionLabels, vinInfo);

        return FordVinAnalyserResultDTO.builder()
                .vinInfo(vinInfo)
                .optionCodes(optionCodes)
                .majorOptionCodes(majorOptionCodes)
                .optionLabels(optionLabels)
                .confirmLabels(confirmLabels)
                .build();
    }

    @Override
    public FordVinAnalyserResultDTO analyseVinCodeByCache(String vinCode) {
        FordVinInfoEntity vinInfo = vinInfoDAO.queryByVinCode(vinCode);
        if (vinInfo == null) {
            log.info("暂无车架号信息缓存结果：{}", vinCode);
            return null;
        }

        String[] interpretationAVSDataArr = StringUtils.split(vinInfo.getInterpretationAVSData(), ";");
        Set<String> optionCodes = Sets.newHashSet(interpretationAVSDataArr);
        Set<String> majorOptionCodes = optionCodes.stream().filter(code -> {
            String[] split = code.split("\\|");
            return split.length > 1 && majorOptionMapper.keySet().contains(split[0]);
        }).collect(Collectors.toSet());

        List<Map<String, String>> optionLabels = getOptionLabelsByAnalyseResult(vinInfo);

        Set<String> confirmLabels = getConfirmLabels(optionLabels, vinInfo);

        return FordVinAnalyserResultDTO.builder()
                .vinInfo(vinInfo)
                .optionCodes(optionCodes)
                .majorOptionCodes(majorOptionCodes)
                .optionLabels(optionLabels)
                .confirmLabels(confirmLabels)
                .build();
    }

    private Set<String> getConfirmLabels(List<Map<String, String>> optionLabels, FordVinInfoEntity vinInfo) {
        if (CollectionUtils.isEmpty(optionLabels) || vinInfo == null) {
            return Collections.emptySet();
        }

        List<String> areaOption = getOptionForArea();

        Set<String> confirmLabels = optionLabels.stream().map(p -> {
            String attributeCode = p.get("attributeCode");
            String labelValue = p.get("label");
            if (!"minor".equalsIgnoreCase(attributeCode)) {
                return labelValue.trim();
            }
            String[] split = labelValue.split(" : ");
            if (split.length != 2) {
                return labelValue.trim();
            }
            return split[1].trim();
        }).collect(Collectors.toSet());

        Set<String> dealAreaOption = Sets.newHashSet();
        //处理地区option
        for (Map<String, String> optionLabel : optionLabels) {
            if ("W".equalsIgnoreCase(optionLabel.get("attributeCode"))) {
                String areaOptionLabel = optionLabel.get("label");
                dealAreaOption = areaOption.stream().filter(p -> !areaOptionLabel.equalsIgnoreCase(p)).map(p -> p.replaceAll("\\(\\+\\)", "(-)")).collect(Collectors.toSet());
            }
        }

        confirmLabels.addAll(dealAreaOption);
        confirmLabels.addAll(areaOption);
        if (StringUtils.isNotBlank(vinInfo.getCatalogCode()) && "CZG".equalsIgnoreCase(vinInfo.getCatalogCode())) {
            confirmLabels.add("MUSTANG");
        }

        //将vinInfo里的信息也加入到验证列表里
        addVinInfoToConfirmLabel(confirmLabels, vinInfo.getEngine());
        addVinInfoToConfirmLabel(confirmLabels, vinInfo.getTransmission());
        addVinInfoToConfirmLabel(confirmLabels, vinInfo.getExteriorPaint());
        addVinInfoToConfirmLabel(confirmLabels, vinInfo.getCabStyle());

        //WD过滤
        if (confirmLabels.stream().anyMatch(p -> p.contains("4WD"))) {
            confirmLabels.add("4WD");
        } else if (confirmLabels.stream().anyMatch(p -> p.contains("2WD"))) {
            confirmLabels.add("2WD");
        } else if (confirmLabels.stream().anyMatch(p -> p.contains("RWD"))) {
            confirmLabels.add("RWD");
        } else if (confirmLabels.stream().anyMatch(p -> p.contains("FWD"))) {
            confirmLabels.add("FWD");
        }

        if (confirmLabels.stream().anyMatch(p -> p.contains("LHD"))) {
            confirmLabels.add("LHD");
        } else if (confirmLabels.stream().anyMatch(p -> p.contains("RHD"))) {
            confirmLabels.add("RHD");
        }

        if (StringUtils.isNotBlank(vinInfo.getEngine()) && vinInfo.getEngine().contains("DURATEC") && vinInfo.getEngine().contains("HE")) {
            confirmLabels.add("DURATEC HE");
            confirmLabels.add("DURATEC-HE");
            confirmLabels.add("DURATEC HE (NON DISI)");
        }

        //特殊情况,注意到有部分是缩写来的,结果变成了过滤条件,就很坑
        //最好的方法应该是匹配到code来进行过滤
        confirmLabels.add("OAT");

        return confirmLabels;
    }

    private void addVinInfoToConfirmLabel(Set<String> confirmLabels, String vinDetailInfo) {
        if (StringUtils.isBlank(vinDetailInfo)) {
            return;
        }
        confirmLabels.add(vinDetailInfo.toUpperCase().trim());
    }

    private List<String> getOptionForArea() {
        if (CollectionUtils.isEmpty(optionForArea)) {
            optionForArea = commonDAO.queryOptionLabelForArea();
        }
        return optionForArea;
    }

    @Override
    public List<EPCCarTypeDTO> queryCarTypes(FordVinAnalyserResultDTO analyseResult) {
        FordVinInfoEntity vinInfo = analyseResult.getVinInfo();
        String modelYear = vinInfo.getModelYear();
        if (StringUtils.isBlank(modelYear)) {
            log.warn("福特查询的vin:{}没有年款", vinInfo.getVinCode());
            return Collections.emptyList();
        }

        Set<String> majorOptionCodes = analyseResult.getMajorOptionCodes();
        String optionKey = majorOptionCodes.stream()
                .sorted(String::compareToIgnoreCase)
                .map(code -> code.split("\\|")[1])
                .collect(Collectors.joining("_"));

        String btrId = commonDAO.queryBtrRelation(optionKey, modelYear);
        if (btrId == null) {
            btrId = Optional.ofNullable(commonDAO.queryBtrRelation(optionKey, null))
                    .map(s -> s.substring(0, s.length() - 4) + modelYear)
                    .orElseGet(() -> "FORDEPC_" + MD5Utils.getMD5Code(optionKey).toUpperCase() + "_" + modelYear);

            String vinCode = vinInfo.getVinCode();
            String catalogCode = vinInfo.getCatalogCode();
            boolean madeInChina = vinCode.startsWith("L");

            FordBuildCatalogEntity buildCatalog = commonDAO.queryBuildCatalogByCode(catalogCode, madeInChina);

            if (buildCatalog == null) {
                // 如果为null只有可能L开头没找到数据，后续不用特意判空
                buildCatalog = commonDAO.queryBuildCatalogByCode(catalogCode, false);
            }

            if (buildCatalog == null) {
                log.warn("福特有新增车系,请检查,vin:{}", vinInfo.getVinCode());
                return Collections.emptyList();
            }

            Map<String, String> majorOptionMapper = analyseResult.getMajorOptionCodes().stream()
                    .collect(Collectors.toMap(s -> s.split("\\|")[0], s -> s.split("\\|")[1]));

            List<Map<String, String>> majorOptionMap = Lists.newArrayList();
            analyseResult.getMajorOptionCodes().forEach(s -> {
                Map<String, String> majorMap = Maps.newHashMap();
                majorMap.put("attribute", s.split("\\|")[0]);
                majorMap.put("code", s.split("\\|")[1]);
                majorOptionMap.add(majorMap);
            });


            Set<FordOptionInfoEntity> fordOptionEntities = commonDAO.queryMajorOptionByCode(majorOptionMap);
            if (CollectionUtils.isEmpty(fordOptionEntities)) {
                log.warn("主属性没有转义,ford_option需要重新清洗才能生成车型");
                return Collections.emptyList();
            }

            Map<String, FordOptionInfoEntity> optionByAttributeCode = fordOptionEntities.stream().collect(Collectors.toMap(FordOptionInfoEntity::getAttributeCode, s -> s));

            String engine = vinInfo.getEngine() == null ? "" : vinInfo.getEngine();
            String engineCode = majorOptionMapper.get("EN");

            String displacement = null;
            Pattern displacementPattern = Pattern.compile("([0-9.]+)");
            Matcher displacementMatcher = displacementPattern.matcher(engine);
            if (displacementMatcher.find()) {
                displacement = displacementMatcher.group(1);
            }

            String carType = null;
            if (optionByAttributeCode.get("VS") != null) {
                carType = optionByAttributeCode.get("VS").getLabel();
                String transLateVersion = versionOptionMapper.get(carType);
                if (StringUtils.isNotBlank(transLateVersion)) {
                    carType = transLateVersion;
                }
            }

            //变速箱信息
            String transmissionType = null;
            String transmissionDesc = null;
            String gearsNum = null;

            FordOptionInfoEntity transOption = optionByAttributeCode.get("TR");
            if (transOption != null) {
                String label = transOption.getLabel();
                if (StringUtils.isNotBlank(label)) {
                    if (label.contains("MAN")) {
                        transmissionType = "手动";
                        transmissionDesc = "手动变速器(MT)";
                    } else if (label.contains("AUTO") || label.contains("DCT") || label.contains("A/T")) {
                        transmissionType = "自动";
                        transmissionDesc = "自动变速器(AT)";
                    }

                    Pattern tranPattern = Pattern.compile("(\\d)+\\s*((SPEED)|(SPD)|(-SPE))");
                    Matcher transMatcher = tranPattern.matcher(label);
                    if (transMatcher.find()) {
                        if ("0".equalsIgnoreCase(transMatcher.group(1))) {
                            gearsNum = "1" + transMatcher.group(1) + "档";
                        } else {
                            gearsNum = transMatcher.group(1) + "档";
                        }
                    }
                }
            }

            //车身款式
            String vehicleType = null;
            String doorNumber = null;
            String bodyModel = null;
            FordOptionInfoEntity bodyStyle = optionByAttributeCode.get("BS");
            if (bodyStyle != null) {
                String label = bodyStyle.getLabel();
                if (StringUtils.isNotBlank(label)) {
                    if (label.contains("SALOON")) {
                        vehicleType = "轿车";
                    } else if (label.contains("CABRIOLET")) {
                        vehicleType = "敞篷跑车";
                    } else if (label.contains("COUPE")) {
                        vehicleType = "轿跑车";
                    } else if (label.contains("VAN")) {
                        vehicleType = "货车";
                    } else if (label.contains("ESTATE")) {
                        vehicleType = "旅行车";
                    }

                    Pattern bsPattern = Pattern.compile("(\\d)+\\s*(DOOR)");
                    Matcher bsMatcher = bsPattern.matcher(label);
                    if (bsMatcher.find()) {
                        doorNumber = getDoorNumber(bsMatcher.group(1));
                    }

                    if ("轿车".equalsIgnoreCase(vehicleType) && "五门".equalsIgnoreCase(doorNumber)) {
                        bodyModel = "两厢";
                    } else if ("轿车".equalsIgnoreCase(vehicleType) && "四门".equalsIgnoreCase(doorNumber)) {
                        bodyModel = "三厢";
                    }

                }
            }

            String driveType = null;
            FordOptionInfoEntity driveMode = optionByAttributeCode.get("DR");
            if (driveMode != null) {
                String label = driveMode.getLabel();
                if (StringUtils.isNotBlank(label)) {
                    if (label.contains("2WD") || label.contains("FWD")) {
                        driveType = "前轮驱动";
                    } else if (label.contains("4WD")) {
                        driveType = "四轮驱动";
                    } else if (label.contains("RWD")) {
                        driveType = "后轮驱动";
                    }
                }
            }

            CarTypeBuilderDTO builder = CarTypeBuilderDTO.builder()
                    .btrId(btrId)
                    .btrBrand(buildCatalog.getBrand())
                    .btrSubBrand(buildCatalog.getSubBrand())
                    .carSystemId(buildCatalog.getCarSystemId())
                    .carSystem(buildCatalog.getCarSystem())
                    .carType(carType)
                    .chassis(engine)
                    .salesName(nullToEmpty(displacement) + " " + nullToEmpty(transmissionDesc) + " " + nullToEmpty(carType))
                    .productTime(vinInfo.getModelYear())
                    .engineType(engineCode)
                    .displacement(displacement)
                    .transmissionType(transmissionType)
                    .transmissionDesc(transmissionDesc)
                    .gearsNum(gearsNum)
                    .vehicleType(vehicleType)
                    .doorNumber(doorNumber)
                    .driveType(driveType)
                    .bodyModel(bodyModel)
                    .build();
            List<CarTypeBuilderDTO> carTypeBuilderDTOs = Lists.newArrayList(builder);
            vinCacheService.saveNewCarType(carTypeBuilderDTOs);
            commonDAO.saveBtrRelation(btrId, optionKey, modelYear);
        }

        EPCCarTypeDTO carType = new EPCCarTypeDTO();
        carType.setBtrId(btrId);
        carType.setVinCode(vinInfo.getVinCode());

        List<EPCCarTypeDTO.ExtendAttributeDTO> extendAttributeDTOS = Lists.newArrayList();
        extendAttributeDTOS.add(new EPCCarTypeDTO.ExtendAttributeDTO("engineDescription", analyseResult.getVinInfo().getEngine(), "发动机描述"));
        extendAttributeDTOS.add(new EPCCarTypeDTO.ExtendAttributeDTO("yearRange", analyseResult.getVinInfo().getModelYear(), "年款,匹配碰撞部位年款范围"));
        carType.setExtendAttrDTOs(extendAttributeDTOS);

        return Lists.newArrayList(carType);
    }

    private String getDoorNumber(String input) {
        switch (input) {
            case "1":
                return "一门";
            case "2":
                return "两门";
            case "3":
                return "三门";
            case "4":
                return "四门";
            case "5":
                return "五门";
            case "6":
                return "六门";
            case "7":
                return "七门";
            case "8":
                return "八门";
            case "9":
                return "九门";
        }
        return null;
    }

    private String nullToEmpty(String str) {
        if (StringUtils.isBlank(str)) {
            return StringUtils.EMPTY;
        }
        return str;
    }

    @Override
    protected List<FordPartsMappingEntity> queryPartsMappings(Set<String> btrPartsNames, FordVinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(btrPartsNames)) {
            return Collections.emptyList();
        }
        return partsMappingDAO.queryByPartsName(btrPartsNames);
    }

    @Override
    public List<FordPartsInfoEntity> queryPartsInfoByPartsMapping(List<FordPartsMappingEntity> partsMappings, FordVinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsMappings)) {
            return Collections.emptyList();
        }

        Set<String> partsCodes = partsMappings.stream().map(FordPartsMappingEntity::getPartsCode).collect(Collectors.toSet());
        return StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE).flatMap(l ->
                queryPartsInfoByPartsCode(new HashSet<>(l), analyseResult).stream()
        ).collect(Collectors.toList());
    }

    @Override
    public List<FordPartsInfoEntity> queryPartsInfoByPartsCode(Set<String> partsCodes) {
        return this.queryPartsInfoByPartsCode(partsCodes, null);
    }

    @Override
    protected List<FordPartsInfoEntity> queryPartsInfoByPartsCode(Set<String> partsCodes,
                                                                  FordVinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsCodes)) {
            return Collections.emptyList();
        }

        String catalogCode = Optional.ofNullable(analyseResult).map(r -> r.getVinInfo().getCatalogCode()).orElse(null);
        return partsInfoDAO.queryByPartsCode(catalogCode, partsCodes);
    }

    @Override
    public List<FordPartsInfoEntity> filterPartsInfoByVinAnalyseResult(List<FordPartsInfoEntity> partsInfo,
                                                                       FordVinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsInfo)) {
            return partsInfo;
        }
        partsInfo = filterPartsInfoByLabel(partsInfo, analyseResult);

        partsInfo = filterPartsInfoBySectionLabel(partsInfo, analyseResult);

        partsInfo = filterPartsInfoByDate(partsInfo, analyseResult);

//        partsInfo = replaceToNewestPart(partsInfo, analyseResult);

        return partsInfo;
    }

    private List<FordPartsInfoEntity> replaceToNewestPart(List<FordPartsInfoEntity> partsInfo,
                                                          FordVinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsInfo) || analyseResult.getVinInfo() == null || StringUtils.isBlank(analyseResult.getVinInfo().getCatalogCode())) {
            return partsInfo;
        }

        Set<String> partsCode = partsInfo.stream().map(FordPartsInfoEntity::getPartsCode).collect(Collectors.toSet());
        List<FordReplaceDTO> replaceInfo = partsInfoDAO.queryNewestReplacePartsCode(analyseResult.getVinInfo().getCatalogCode(), partsCode);

        if (CollectionUtils.isEmpty(replaceInfo)) {
            return partsInfo;
        }

        Map<String, String> replaceMap = replaceInfo.stream().collect(Collectors.toMap(p -> p.getCatalogCode() + "_" + p.getSectionCode() + "_" + p.getPartsCode(), FordReplaceDTO::getNewestCode, (e1, e2)->e1));
        partsInfo.forEach(p->{
            String newestCode = replaceMap.get(p.getCatalogCode() + "_" + p.getSectionCode() + "_" + p.getPartsCode());
            if (StringUtils.isNotBlank(newestCode)) {
                p.setPartsCode(newestCode);
            }
        });

        return partsInfo;
    }

    @Override
    protected List<FordPartsMappingEntity> queryPartsMappingByPartsInfoAndAnalyseResult(Map<String, List<FordPartsInfoEntity>> partsInfoGroup, FordVinAnalyserResultDTO analyseResult) {
        if (MapUtils.isEmpty(partsInfoGroup)) {
            return Collections.emptyList();
        }

        Set<String> partsCodes = partsInfoGroup.values().stream().flatMap(l -> l.stream().map(FordPartsInfoEntity::getPartsCode)).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(partsCodes)) {
            return Collections.emptyList();
        }
        return StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE).flatMap(l ->
                partsMappingDAO.queryByPartsCode(l).stream()
        ).collect(Collectors.toList());
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initMajorOptionMapper();
        initVersionOptionMapper();
        initColorFeatureMapper();
    }

    @Override
    public Map<String, List<FordPartsInfoEntity>> filterPartsInfoByVinAnalyseResult(Map<String, List<FordPartsInfoEntity>> partsInfoGroupByPartsName,
                                                                                    FordVinAnalyserResultDTO analyseResult) {
        if (MapUtils.isEmpty(partsInfoGroupByPartsName)) {
            return Collections.emptyMap();
        }

        return partsInfoGroupByPartsName.entrySet().stream().peek(entry -> {
            List<FordPartsInfoEntity> value = entry.getValue();
            String buildDate = Optional.ofNullable(analyseResult).map(res -> {
                String buildDate1 = res.getVinInfo().getBuildDate();
                return StringUtils.isNotBlank(buildDate1) ? buildDate1 : res.getVinInfo().getDisplayBuildDate();
            }).orElse(null);
            if (value.size() > 1 && StringUtils.isNotBlank(buildDate)) {
                List<FordPartsInfoEntity> partsInfo = filterPartsInfoByBuildDate(buildDate, value);
                if (!partsInfo.isEmpty()) {
                    entry.setValue(partsInfo);
                }
            }
        }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    private List<FordPartsInfoEntity> filterPartsInfoByLabel(List<FordPartsInfoEntity> partsInfos,
                                                             FordVinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsInfos)) {
            return partsInfos;
        }

        Map<String, List<FordPartsInfoEntity>> labelMapping =
                partsInfos.stream().collect(Collectors.groupingBy(FordPartsInfoEntity::getLabel));

        Set<String> labels = labelMapping.keySet();
        Set<String> filterLabels =
                labels.stream().filter(label -> cannotBeRemoved(label, analyseResult)).collect(Collectors.toSet());

        return labelMapping.entrySet().stream()
                .filter(entry -> filterLabels.contains(entry.getKey()))
                .flatMap(entry -> entry.getValue().stream())
                .collect(Collectors.toList());
    }

    private List<FordPartsInfoEntity> filterPartsInfoByBuildDate(String buildDateStr, List<FordPartsInfoEntity> partsInfos) {
        String dataPattern = "yyyyMMdd";
        Date buildDate = parseDateByPattern(buildDateStr, dataPattern);
        if (buildDate == null) {
            return partsInfos;
        }
        return partsInfos.stream().filter(info -> {
            String beginDateStr = info.getBeginDate();
            String endDateStr = info.getEndDate();

            Date beginDate = parseDateByPattern(beginDateStr, dataPattern);
            Date endDate = parseDateByPattern(endDateStr, dataPattern);

            return (beginDate == null || beginDate.before(buildDate)) && ((endDate == null || endDate.after(buildDate)));
        }).collect(Collectors.toList());
    }

    private Date parseDateByPattern(String dateStr, String datePattern) {
        try {
            return StringUtils.isBlank(dateStr) ? null : DateUtils.parseDate(dateStr, datePattern);
        } catch (Exception e) {
            log.error("日期解析错误：dateStr - {}， datePattern - {}", dateStr, datePattern);
            return null;
        }
    }

    private List<FordPartsInfoEntity> filterPartsInfoByDate(List<FordPartsInfoEntity> partsInfos,
                                                            FordVinAnalyserResultDTO analyseResult) {
        String buildDate = Optional.ofNullable(analyseResult).map(res -> {
            String buildDate1 = res.getVinInfo().getBuildDate();
            return StringUtils.isNotBlank(buildDate1) ? buildDate1 : res.getVinInfo().getDisplayBuildDate();
        }).orElse(null);
        if (StringUtils.isBlank(buildDate)) {
            return partsInfos;
        }

        return partsInfos.stream().filter(info -> {
            String beginDate = info.getBeginDate();
            String endDate = info.getEndDate();
            return matchPartsRange(buildDate, beginDate, endDate);
        }).collect(Collectors.toList());
    }

    private boolean matchPartsRange(String modelYeer, String beginDate, String endDate) {
        if (StringUtils.isBlank(modelYeer)) {
            return true;
        }

        if ((StringUtils.isNotBlank(beginDate) && Integer.valueOf(beginDate) > Integer.valueOf(modelYeer))
                || (StringUtils.isNotBlank(endDate) && Integer.valueOf(endDate) < Integer.valueOf(modelYeer))) {
            return false;
        }
        return true;
    }

    private List<Map<String, String>> getOptionLabelsByAnalyseResult(FordVinInfoEntity vinInfo) {
        if (vinInfo == null) {
            return Collections.emptyList();
        }

        String interpretationAVSData = vinInfo.getInterpretationAVSData();
        List<Map<String, String>> carOptions = Stream.of(StringUtils.split(interpretationAVSData, ";")).map(str -> {
            String[] strArr = str.split("\\|");
            Map<String, String> item = new HashMap<>();

            if (majorOptionMapper.keySet().contains(strArr[0])) {
                item.put("attribute", strArr[0]);
            } else {
                item.put("attribute", "minor");
            }
            item.put("code", strArr[1]);
            return item;
        }).collect(Collectors.toList());

        return commonDAO.queryOptionLabelByCode(carOptions);
    }

    private List<FordPartsInfoEntity> filterPartsInfoBySectionLabel(List<FordPartsInfoEntity> partsInfos,
                                                                    FordVinAnalyserResultDTO analyseResult) {

        String vinCode = Optional.ofNullable(analyseResult).map(a -> a.getVinInfo().getVinCode()).orElse(null);
        if (StringUtils.isBlank(vinCode)) {
            return partsInfos;
        }

        List<Map<String, String>> sections = partsInfos.stream().map(p -> {
            Map<String, String> item = new HashMap<>();
            item.put("catalogCode", p.getCatalogCode());
            item.put("sectionCode", p.getSectionCode());
            return item;
        }).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(sections)) {
            return partsInfos;
        }

        Set<FordSectionInfoEntity> sectionInfo = commonDAO.querySectionByAttributeAndCode(sections);

        Set<String> sectionKeyByFilter = sectionInfo.stream().filter(section -> {
            String label = section.getLabel();
            if (StringUtils.isEmpty(label)) {
                return true;
            }

            //发动机过滤
            if (!filterSectionByEngine(label, analyseResult)) {
                return false;
            }

            //变速器过滤
            if (!filterSectionByTrans(label, analyseResult)) {
                return false;
            }

            //时间限制
            String buildDate = Optional.ofNullable(analyseResult).map(res -> {
                String buildDate1 = res.getVinInfo().getBuildDate();
                return StringUtils.isNotBlank(buildDate1) ? buildDate1 : res.getVinInfo().getDisplayBuildDate();
            }).orElse(null);
            String dateLabel = "\\(";
            if (StringUtils.isNotBlank(buildDate)) {
                String beginDate = "";
                Pattern beginPattern = Pattern.compile("FROM\\s[1-2]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])");
                Matcher beginMatcher = beginPattern.matcher(label);
                if (beginMatcher.find()) {
                    dateLabel = dateLabel + beginMatcher.group(0);
                    beginDate = beginMatcher.group(0).replaceAll("FROM ", "").replaceAll("-", "");
                }
                String endDate = "";

                Pattern endPattern = Pattern.compile("TO\\s[1-2]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])");
                Matcher endMatcher = endPattern.matcher(label);
                if (endMatcher.find()) {
                    if ("\\(".equals(dateLabel)) {
                        dateLabel = dateLabel + endMatcher.group(0);
                    } else {
                        dateLabel = dateLabel + " " + endMatcher.group(0);
                    }
                    endDate = endMatcher.group(0).replaceAll("TO ", "").replaceAll("-", "");
                }
                if ((StringUtils.isNotBlank(beginDate) && Integer.valueOf(beginDate) > Integer.valueOf(buildDate))
                        || (StringUtils.isNotBlank(endDate) && Integer.valueOf(endDate) < Integer.valueOf(buildDate))) {
                    return false;
                }
            }
            dateLabel = dateLabel + "\\)";
            String filterLabel = label;
            if (!"\\(\\)".equals(dateLabel)) {
                filterLabel = filterLabel.replaceAll(dateLabel, "");
            }

            return cannotBeRemoved(filterLabel.trim(), analyseResult);
        }).map(s -> s.getCatalogCode() + "|" + s.getCode()).collect(Collectors.toSet());
        if (sectionKeyByFilter.isEmpty()) {
            return Collections.emptyList();
        }

        return partsInfos.stream().filter(p -> sectionKeyByFilter.contains(p.getCatalogCode() + "|" + p.getSectionCode())).collect(Collectors.toList());
    }

    private boolean filterSectionByTrans(String label, FordVinAnalyserResultDTO analyseResult) {
        if (!label.contains("TRANS") && !label.contains("A/T")) {
            return true;
        }

        List<Map<String, String>> transOption = analyseResult.getOptionLabels().stream().filter(p -> p.get("attributeCode").equalsIgnoreCase("TR")).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(transOption)) {
            return true;
        }
        String transFilter = transOption.get(0).get("label");
        if (StringUtils.isBlank(transFilter)) {
            return true;
        }

        if (label.contains("MAN")) {
            return transFilter.contains("MAN");
        }
        if (label.contains("AUTO") || label.contains("DCT") || label.contains("A/T")) {
            return transFilter.contains("AUTO") || transFilter.contains("DCT") || transFilter.contains("A/T");
        }
        return true;
    }

    private boolean filterSectionByEngine(String label, FordVinAnalyserResultDTO analyseResult) {
        if (!label.contains("DURATEC")) {
            return true;
        }

        FordVinInfoEntity vinInfo = analyseResult.getVinInfo();
        if (StringUtils.isNotBlank(vinInfo.getEngine()) && vinInfo.getEngine().contains("DURATEC") && vinInfo.getEngine().contains("HE")) {
            return !label.contains("DURATEC VE") && !label.contains("DURATEC-VE");
        }
        return true;
    }

    private boolean cannotBeRemoved(String label, FordVinAnalyserResultDTO analyserResult) {
        if (CollectionUtils.isEmpty(analyserResult.getConfirmLabels())) {
            return true;
        }

        String filterValue = getFilterValue(label);
        if (StringUtils.isBlank(filterValue)) {
            return true;
        }

        Set<String> filterLabel = Sets.newHashSet(filterValue.split(" OR "));
        return filterLabel.stream().anyMatch(p -> {
            Set<String> filterLab = Sets.newHashSet(p.split(", "));
            return filterLab.stream().allMatch(l ->
                    analyserResult.getConfirmLabels().contains(l.trim()) || l.toUpperCase().contains("MARKETS")
            );
        });
    }

    private String getFilterValue(String label) {
        if (!label.endsWith(")")) {
            return null;
        }
        String[] split = label.split("");
        int preCount = 0;
        int suffixCount = 0;
        String filterValue = "";
        for (int i = split.length - 2; i >= 0; i--) {
            if (split[i].equals(")")) {
                suffixCount++;
            } else if (split[i].equals("(")) {
                preCount++;
            }
            if (preCount - suffixCount == 1) {
                filterValue = label.substring(i + 1, split.length - 1);
                break;
            }
        }
        return filterValue;
    }

    @Override
    protected Map<String, List<EPCPartsImageDTO>> queryPartsImage(Map<String, List<FordPartsInfoEntity>> partsInfoGroup, FordVinAnalyserResultDTO analyseResult) {
        if (MapUtils.isEmpty(partsInfoGroup)) {
            return Collections.emptyMap();
        }

        List<FordPartsInfoEntity> partsInfoEntities = partsInfoGroup.values().stream().filter(CollectionUtils::isNotEmpty).flatMap(Collection::stream).collect(Collectors.toList());

        List<FordPartsInfoEntity> partsList = partsInfoEntities.stream().filter(p -> StringUtils.isNotBlank(p.getCatalogCode()) && StringUtils.isNotBlank(p.getSectionCode()) && StringUtils.isNotBlank(p.getCallout())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(partsList)) {
            return Collections.emptyMap();
        }

        List<FordImageDTO> fordImageDTOS = StreamKit.partitionStream(partsList, 100).flatMap(p -> partsInfoDAO.queryByPartsInfo(p).stream()).collect(Collectors.toList());

        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(o -> o, p -> {
            List<FordPartsInfoEntity> partsInfo = partsInfoGroup.get(p);
            if (CollectionUtils.isEmpty(partsInfo)) {
                return Collections.emptyList();
            }

            Set<EPCPartsImageDTO> epcPartsImageDTOS = Sets.newHashSet();
            partsInfo.forEach(l -> {
                List<FordImageDTO> imageDTOS = fordImageDTOS.stream().filter(i -> i.getCatalogCode().equalsIgnoreCase(l.getCatalogCode())
                        && i.getSectionCode().equalsIgnoreCase(l.getSectionCode())
                        && i.getCallout().equalsIgnoreCase(l.getCallout())).collect(Collectors.toList());
                if (CollectionUtils.isEmpty(imageDTOS)) {
                    return;
                }
                FordImageDTO fordImageDTO = imageDTOS.get(0);

                EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();

                if (StringUtils.isBlank(fordImageDTO.getChLabel()) || fordImageDTO.getChLabel().contains("?")) {
                    epcPartsImageDTO.setImageName(fordImageDTO.getLabel());
                } else {
                    epcPartsImageDTO.setImageName(fordImageDTO.getChLabel());
                }
                String imgUrl = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.FORD, fordImageDTO.getImagePath());

                epcPartsImageDTO.setImageUrl(imgUrl);
                epcPartsImageDTO.setImageCode(fordImageDTO.getSectionCode());

                List<EPCPartsImagePositionDTO> epcPartsImagePositionDTOs = imageDTOS.stream().map(q -> {
                    EPCPartsImagePositionDTO positionDTO = new EPCPartsImagePositionDTO();

                    EPCImagePointDTO topLeft = new EPCImagePointDTO();
                    topLeft.setX(q.getTopLeftX());
                    topLeft.setY(q.getTopLeftY());

                    EPCImagePointDTO bottomRight = new EPCImagePointDTO();
                    bottomRight.setX(q.getBottomRightX());
                    bottomRight.setY(q.getBottomRightY());

                    positionDTO.setTopLeft(topLeft);
                    positionDTO.setBottomRight(bottomRight);
                    positionDTO.setPosition(q.getCallout());

                    return positionDTO;
                }).collect(Collectors.toList());
                epcPartsImageDTO.setEpcPartsImagePositionDTOs(epcPartsImagePositionDTOs);

                epcPartsImageDTOS.add(epcPartsImageDTO);
            });

            return new ArrayList<>(epcPartsImageDTOS);
        }));
    }

    @Override
    protected Map<String, String> queryInstallNum(Map<String, List<FordPartsInfoEntity>> partsInfoGroup, FordVinAnalyserResultDTO analyseResult) {
        if (MapUtils.isEmpty(partsInfoGroup)) {
            return Collections.emptyMap();
        }
        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(e -> e, p -> {
            List<FordPartsInfoEntity> entities = partsInfoGroup.get(p);
            if (CollectionUtils.isEmpty(entities)) {
                return StringUtils.EMPTY;
            }
            Set<String> installNum = entities.stream().filter(z -> StringUtils.isNotBlank(z.getQty())).map(FordPartsInfoEntity::getQty).collect(Collectors.toSet());
            return combineInstallNum(installNum);
        }));
    }

    private String combineInstallNum(Set<String> installNum) {
        if (CollectionUtils.isEmpty(installNum)) {
            return StringUtils.EMPTY;
        }

        String str = "";
        for (String insNum : installNum) {
            if (StringUtils.isNotBlank(insNum)) {
                str = insNum + "/";
            }
        }
        if (StringUtils.isNotBlank(str)) {
            str = str.substring(0, str.length() - 1);
        }
        return str;
    }

    @Override
    protected Map<String, List<BasePartsImageInfoDTO>> queryEPCImageInfo(Set<String> sortNames, FordVinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(sortNames) || analyseResult == null || analyseResult.getVinInfo() == null || StringUtils.isBlank(analyseResult.getVinInfo().getCatalogCode())) {
            return Collections.emptyMap();
        }
        List<FordSortImageDTO> fordSortImageDTOS = imageDAO.queryImageBySortName(sortNames, analyseResult.getVinInfo().getCatalogCode());
        if (CollectionUtils.isEmpty(fordSortImageDTOS)) {
            return Collections.emptyMap();
        }

        Set<String> imageCodes = fordSortImageDTOS.stream().map(FordSortImageDTO::getImageCode).collect(Collectors.toSet());

        List<FordSortImageDTO> imageDTOS = imageDAO.queryImageInfoByCatalog(analyseResult.getVinInfo().getCatalogCode());
        imageDTOS = filterSectionByLabels(imageDTOS, analyseResult);
        if (CollectionUtils.isEmpty(imageDTOS)) {
            return Collections.emptyMap();
        }

        imageDTOS.removeIf(p -> !imageCodes.contains(p.getImageCode()));
        if (CollectionUtils.isEmpty(imageDTOS)) {
            return Collections.emptyMap();
        }

        imageDTOS.forEach(p -> {
            if (StringUtils.isBlank(p.getChLabel()) || p.getChLabel().contains("?")) {
                p.setImageName(p.getLabel());
            } else {
                p.setImageName(p.getChLabel());
            }
            String imgUrl = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.FORD, p.getImagePath());
            p.setImageUrl(imgUrl);
        });

        Map<String, List<FordSortImageDTO>> imageCodeMap = imageDTOS.stream().collect(Collectors.groupingBy(FordSortImageDTO::getImageCode));
        Map<String, List<FordSortImageDTO>> mapBySortName = fordSortImageDTOS.stream().collect(Collectors.groupingBy(FordSortImageDTO::getSortName));
        return sortNames.stream().collect(Collectors.toMap(e -> e, sortName -> {
            List<FordSortImageDTO> sortImageDTOS = mapBySortName.get(sortName);
            if (CollectionUtils.isEmpty(sortImageDTOS)) {
                return Collections.emptyList();
            }
            return sortImageDTOS.stream().filter(p -> StringUtils.isNotBlank(p.getImageCode()))
                    .filter(p -> CollectionUtils.isNotEmpty(imageCodeMap.get(p.getImageCode())))
                    .flatMap(p -> imageCodeMap.get(p.getImageCode()).stream()).distinct().collect(Collectors.toList());
        }));
    }

    private List<FordSortImageDTO> filterSectionByLabels(List<FordSortImageDTO> imageDTOS, FordVinAnalyserResultDTO analyseResult) {
        String vinCode = Optional.ofNullable(analyseResult).map(a -> a.getVinInfo().getVinCode()).orElse(null);
        if (StringUtils.isBlank(vinCode)) {
            return imageDTOS;
        }

        if (CollectionUtils.isEmpty(imageDTOS)) {
            return imageDTOS;
        }

        return imageDTOS.stream().filter(section -> {
            String label = section.getLabel();
            if (StringUtils.isEmpty(label)) {
                return true;
            }

            //发动机过滤
            if (!filterSectionByEngine(label, analyseResult)) {
                return false;
            }

            //变速器过滤
            if (!filterSectionByTrans(label, analyseResult)) {
                return false;
            }

            //时间限制
            String buildDate = Optional.ofNullable(analyseResult).map(res -> {
                String buildDate1 = res.getVinInfo().getBuildDate();
                return StringUtils.isNotBlank(buildDate1) ? buildDate1 : res.getVinInfo().getDisplayBuildDate();
            }).orElse(null);
            String dateLabel = "\\(";
            if (StringUtils.isNotBlank(buildDate)) {
                String beginDate = "";
                Pattern beginPattern = Pattern.compile("FROM\\s[1-2]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])");
                Matcher beginMatcher = beginPattern.matcher(label);
                if (beginMatcher.find()) {
                    dateLabel = dateLabel + beginMatcher.group(0);
                    beginDate = beginMatcher.group(0).replaceAll("FROM ", "").replaceAll("-", "");
                }
                String endDate = "";

                Pattern endPattern = Pattern.compile("TO\\s[1-2]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])");
                Matcher endMatcher = endPattern.matcher(label);
                if (endMatcher.find()) {
                    if ("\\(".equals(dateLabel)) {
                        dateLabel = dateLabel + endMatcher.group(0);
                    } else {
                        dateLabel = dateLabel + " " + endMatcher.group(0);
                    }
                    endDate = endMatcher.group(0).replaceAll("TO ", "").replaceAll("-", "");
                }
                if ((StringUtils.isNotBlank(beginDate) && Integer.valueOf(beginDate) > Integer.valueOf(buildDate))
                        || (StringUtils.isNotBlank(endDate) && Integer.valueOf(endDate) < Integer.valueOf(buildDate))) {
                    return false;
                }
            }
            dateLabel = dateLabel + "\\)";
            String filterLabel = label;
            if (!"\\(\\)".equals(dateLabel)) {
                filterLabel = filterLabel.replaceAll(dateLabel, "");
            }

            return cannotBeRemoved(filterLabel.trim(), analyseResult);
        }).distinct().collect(Collectors.toList());
    }

    @Override
    protected List<FordPartsInfoEntity> queryPartsInfoByImageCode(Set<String> imageCodes, FordVinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(imageCodes) || analyseResult == null || analyseResult.getVinInfo() == null || StringUtils.isBlank(analyseResult.getVinInfo().getCatalogCode())) {
            return Collections.emptyList();
        }

        Set<String> partsCode = partsInfoDAO.queryPartsCodeByCatalogAndSection(analyseResult.getVinInfo().getCatalogCode(), imageCodes);
        if (CollectionUtils.isEmpty(partsCode)) {
            return Collections.emptyList();
        }

        List<FordPartsInfoEntity> partsInfo = queryPartsInfoByPartsCode(partsCode, analyseResult);
        if (CollectionUtils.isEmpty(partsInfo)) {
            return Collections.emptyList();
        }

        return filterPartsInfoByVinAnalyseResult(partsInfo, analyseResult);
    }

    @Override
    protected List<EPCPartsImageDTO> queryEPCImageInfoByImageCode(Set<String> imageCodes, FordVinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(imageCodes) || analyseResult == null || analyseResult.getVinInfo() == null || StringUtils.isBlank(analyseResult.getVinInfo().getCatalogCode())) {
            return Collections.emptyList();
        }

        List<FordSortImageDTO> imageDTOS = imageDAO.queryImageInfoByCatalog(analyseResult.getVinInfo().getCatalogCode());
        imageDTOS = filterSectionByLabels(imageDTOS, analyseResult);
        if (CollectionUtils.isEmpty(imageDTOS)) {
            return Collections.emptyList();
        }

        imageDTOS.removeIf(p -> !imageCodes.contains(p.getImageCode()));
        if (CollectionUtils.isEmpty(imageDTOS)) {
            return Collections.emptyList();
        }

        imageDTOS.forEach(p -> {
            if (StringUtils.isBlank(p.getChLabel()) || p.getChLabel().contains("?")) {
                p.setImageName(p.getLabel());
            } else {
                p.setImageName(p.getChLabel());
            }
            String imgUrl = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.FORD, p.getImagePath());
            p.setImageUrl(imgUrl);
        });

        List<FordImageDTO> imagePostionDTOs = imageDAO.queryHotPointsByCatalogAndSection(analyseResult.getVinInfo().getCatalogCode(), imageDTOS.stream().map(FordSortImageDTO::getImageCode).collect(Collectors.toSet()));
        if (CollectionUtils.isEmpty(imagePostionDTOs)) {
            return Collections.emptyList();
        }

        Map<String, List<FordImageDTO>> hotPointsByImageCode = imagePostionDTOs.stream().collect(Collectors.groupingBy(FordImageDTO::getSectionCode));
        Map<String, FordSortImageDTO> imgDTOByImageCode = imageDTOS.stream().collect(Collectors.toMap(FordSortImageDTO::getImageCode, o -> o, (v1, v2) -> v1));
        return imageCodes.stream().filter(p -> CollectionUtils.isNotEmpty(hotPointsByImageCode.get(p)) && imgDTOByImageCode.get(p) != null)
                .map(p -> {
                    EPCPartsImageDTO epcPartsImageDTO = new EPCPartsImageDTO();
                    epcPartsImageDTO.setImageCode(p);
                    FordSortImageDTO fordSortImageDTO = imgDTOByImageCode.get(p);
                    epcPartsImageDTO.setImageUrl(fordSortImageDTO.getImageUrl());
                    epcPartsImageDTO.setImageName(fordSortImageDTO.getImageName());
                    List<FordImageDTO> fordImageDTOS = hotPointsByImageCode.get(p);

                    List<EPCPartsImagePositionDTO> epcPartsImagePositionDTOs = fordImageDTOS.stream().map(position -> {
                        EPCPartsImagePositionDTO epip = new EPCPartsImagePositionDTO();
                        EPCImagePointDTO topLeft = new EPCImagePointDTO();
                        EPCImagePointDTO bottomRight = new EPCImagePointDTO();
                        topLeft.setX(position.getTopLeftX());
                        topLeft.setY(position.getTopLeftY());
                        bottomRight.setX(position.getBottomRightX());
                        bottomRight.setY(position.getBottomRightY());
                        epip.setPosition(position.getCallout());
                        epip.setTopLeft(topLeft);
                        epip.setBottomRight(bottomRight);
                        return epip;
                    }).collect(Collectors.toList());

                    epcPartsImageDTO.setEpcPartsImagePositionDTOs(epcPartsImagePositionDTOs);

                    return epcPartsImageDTO;
                }).collect(Collectors.toList());
    }

    @Override
    public Map<String, String> queryEpcPartsName (Map<String, List<FordPartsInfoEntity>> partsInfoGroup, FordVinAnalyserResultDTO analyseResult) {
        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(e -> e, p->{
            List<FordPartsInfoEntity> fordPartsInfoEntities = partsInfoGroup.get(p);
            if (CollectionUtils.isEmpty(fordPartsInfoEntities)) {
                return StringUtils.EMPTY;
            }
            List<String> epcPartsNames = fordPartsInfoEntities.stream().map(l->{
                if (StringUtils.isBlank(l.getChLabel()) || l.getChLabel().contains("?")) {
                    return l.getLabel();
                } else {
                    return l.getChLabel();
                }
            }).collect(Collectors.toList());
            if(CollectionUtils.isEmpty(epcPartsNames)) {
                return StringUtils.EMPTY;
            }
            return epcPartsNames.get(0);
        }));
    }

    @Override
    protected Map<String, Set<String>> queryReplaceCodes(Set<String> partsCodes, FordVinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsCodes)
                || Objects.isNull(analyseResult)
                || analyseResult.getVinInfo()==null
                || StringUtils.isBlank(analyseResult.getVinInfo().getCatalogCode())) {
            return Collections.emptyMap();
        }

        List<FordReplaceDTO> replaceInfo = partsInfoDAO.queryReplacePartsCode(analyseResult.getVinInfo().getCatalogCode(), partsCodes);
        if (CollectionUtils.isEmpty(replaceInfo)) {
            return Collections.emptyMap();
        }

        return replaceInfo.stream().collect(Collectors.toMap(FordReplaceDTO::getPartsCode,
                p->Sets.newHashSet(p.getReplaceCode()),
                (e1, e2)->{
                    e1.addAll(e2);
                    return e1;
                }));
    }

    @Override
    public Map<String, String> queryColorRemark(Map<String, List<FordPartsInfoEntity>> partsInfoGroup, FordVinAnalyserResultDTO analyseResult) {
        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(e -> e, p -> {
            List<FordPartsInfoEntity> partsInfo = partsInfoGroup.get(p);
            if (CollectionUtils.isEmpty(partsInfo)) {
                return StringUtils.EMPTY;
            }
            Set<String> colorRemarks = partsInfo.stream()
                    .map(parts -> {
                        String firstMatchKey = colorMapper.keySet().stream()
                                .sorted(Comparator.comparing(String::length).reversed())
                                .filter(l -> parts.getLabel().contains(l))
                                .findFirst().orElse(null);
                        if (StringUtils.isBlank(firstMatchKey)) {
                            return null;
                        }
                        return colorMapper.get(firstMatchKey);
                    })
                    .filter(StringUtils::isNotBlank)
                    .collect(Collectors.toSet());
            return combineString(colorRemarks);
        }));
    }

    private String combineString(Set<String> set) {
        String str = "";
        if (CollectionUtils.isNotEmpty(set)) {
            for (String remark : set) {
                if (StringUtils.isNotBlank(remark)) {
                    str = remark + "/";
                }
            }
            if (StringUtils.isNotBlank(str)) {
                str = str.substring(0, str.length() - 1);
            }
        }
        return str;
    }

    @Override
    protected Map<String, Set<String>> queryFeatures(Map<String, List<FordPartsInfoEntity>> partsInfoGroup, FordVinAnalyserResultDTO analyseResult) {
        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(e -> e, p -> {
            List<FordPartsInfoEntity> partsInfo = partsInfoGroup.get(p);
            if (CollectionUtils.isEmpty(partsInfo)) {
                return Collections.emptySet();
            }
            return partsInfo.stream()
                    .map(parts -> {
                        String firstMatchKey = featureMapper.keySet().stream()
                                .sorted(Comparator.comparing(String::length).reversed())
                                .filter(l -> parts.getLabel().contains(l))
                                .findFirst().orElse(null);
                        if (StringUtils.isBlank(firstMatchKey)) {
                            return null;
                        }
                        return featureMapper.get(firstMatchKey);
                    })
                    .filter(StringUtils::isNotBlank)
                    .collect(Collectors.toSet());
        }));
    }

    @Override
    protected List<PartsAssemblyDTO> queryPartsAssembly(Set<String> imageCodes, Set<String> partsNames, FordVinAnalyserResultDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsNames) || CollectionUtils.isEmpty(imageCodes) || analyseResult == null) {
            return Collections.emptyList();
        }

        List<EPCCarTypeDTO> epcCarTypeDTOs = queryCarTypes(analyseResult);
        if (CollectionUtils.isEmpty(epcCarTypeDTOs)) {
            return Collections.emptyList();
        }

        EPCCarTypeDTO epcCarTypeDTO = epcCarTypeDTOs.get(0);
        String btrId = epcCarTypeDTO.getBtrId();

        List<CarTypeDTO> carTypeDTOS = carTypeService.findCarTypeByBtrIds(Lists.newArrayList(btrId));
        if (CollectionUtils.isEmpty(carTypeDTOS)) {
            log.info("btrId:{}查询不到车型信息", btrId);
            return Collections.emptyList();
        }

        String carSystem = carTypeDTOS.get(0).getCarSystem();
        String engineDescription = getTypeFromEPCCarTypeResult(epcCarTypeDTO, "engineDescription");
        String yearRange = getTypeFromEPCCarTypeResult(epcCarTypeDTO, "yearRange");

        List<PartsAssemblyDTO> partsAssemblyDTOS = StreamKit.partitionStream(partsNames, MySQLVariables.EQ_RANGE_INDEX_DIVE_LIMIT)
                .flatMap(partsName -> assemblyDAO.queryAssemblyInfo(partsName, carSystem, engineDescription, yearRange).stream()
                ).distinct().collect(Collectors.toList());

        if (CollectionUtils.isEmpty(partsAssemblyDTOS)) {
            return Collections.emptyList();
        }

        List<PartsAssemblyDTO> assemblyDTOS = Lists.newArrayList();

        imageCodes.forEach(imageCode->
            partsAssemblyDTOS.forEach(assemblyDTO->{
                PartsAssemblyDTO pad = PartsAssemblyDTO.builder()
                        .assPartsName(assemblyDTO.getAssPartsName())
                        .nonAssPartsName(assemblyDTO.getNonAssPartsName())
                        .imageCode(imageCode)
                        .build();
                assemblyDTOS.add(pad);
            })
        );

        return assemblyDTOS;
    }

    private String getTypeFromEPCCarTypeResult(EPCCarTypeDTO epcCarTypeDTO, String code) {
        List<EPCCarTypeDTO.ExtendAttributeDTO> extendAttrDTOs = epcCarTypeDTO.getExtendAttrDTOs();
        if (CollectionUtils.isEmpty(extendAttrDTOs)) {
            return null;
        }
        for (EPCCarTypeDTO.ExtendAttributeDTO extendAttrDTO : extendAttrDTOs) {
            if (extendAttrDTO.getCode() != null && extendAttrDTO.getCode().equals(code)) {
                return (String) extendAttrDTO.getValue();
            }
        }
        return null;
    }






}